/*
 * Copyright (c) 2016, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include "gtest/gtest.h"
#include <string>
#include "protocol_handler/protocol_handler.h"
#include "protocol_handler/protocol_handler_impl.h"
#include "protocol/common.h"
#include "protocol_handler/control_message_matcher.h"
#include "protocol_handler/mock_protocol_handler.h"
#include "protocol_handler/mock_protocol_observer.h"
#include "protocol_handler/mock_protocol_handler_settings.h"
#include "protocol_handler/mock_session_observer.h"
#include "connection_handler/mock_connection_handler.h"
#include "security_manager/mock_security_manager.h"
#include "security_manager/mock_ssl_context.h"
#include "transport_manager/mock_transport_manager.h"
#include "utils/make_shared.h"

namespace test {
namespace components {
namespace protocol_handler_test {

// Id passed as NULL for new session establishing
#define NEW_SESSION_ID 0u
#define SESSION_START_REJECT 0u
// Protocol Handler Entities
using protocol_handler::ProtocolHandlerImpl;
using protocol_handler::ServiceType;
using protocol_handler::RawMessage;
using protocol_handler::RawMessagePtr;
using protocol_handler::PROTECTION_ON;
using protocol_handler::PROTECTION_OFF;
using protocol_handler::PROTOCOL_VERSION_1;
using protocol_handler::PROTOCOL_VERSION_2;
using protocol_handler::PROTOCOL_VERSION_3;
using protocol_handler::PROTOCOL_VERSION_MAX;
using protocol_handler::FRAME_TYPE_CONTROL;
using protocol_handler::FRAME_TYPE_SINGLE;
using protocol_handler::FRAME_TYPE_FIRST;
using protocol_handler::FRAME_TYPE_CONSECUTIVE;
using protocol_handler::FRAME_TYPE_MAX_VALUE;
using protocol_handler::MAXIMUM_FRAME_DATA_V2_SIZE;
using protocol_handler::FRAME_DATA_START_SERVICE;
using protocol_handler::FRAME_DATA_START_SERVICE_ACK;
using protocol_handler::FRAME_DATA_END_SERVICE_NACK;
using protocol_handler::FRAME_DATA_END_SERVICE_ACK;
using protocol_handler::FRAME_DATA_END_SERVICE;
using protocol_handler::FRAME_DATA_HEART_BEAT;
using protocol_handler::FRAME_DATA_HEART_BEAT_ACK;
using protocol_handler::FRAME_DATA_SINGLE;
using protocol_handler::FRAME_DATA_FIRST;
using protocol_handler::FRAME_DATA_LAST_CONSECUTIVE;
using protocol_handler::kRpc;
using protocol_handler::kControl;
using protocol_handler::kAudio;
using protocol_handler::kMobileNav;
using protocol_handler::kBulk;
using protocol_handler::kInvalidServiceType;
// For TM states
using transport_manager::TransportManagerListener;
using transport_manager::E_SUCCESS;
using transport_manager::DeviceInfo;
// For CH entities
using connection_handler::DeviceHandle;
// Google Testing Framework Entities
using ::testing::Return;
using ::testing::ReturnRefOfCopy;
using ::testing::ReturnNull;
using ::testing::AnyOf;
using ::testing::DoAll;
using ::testing::_;
using ::testing::Invoke;
using ::testing::SetArgReferee;
using ::testing::SetArgPointee;

typedef std::vector<uint8_t> UCharDataVector;

class ProtocolHandlerImplTest : public ::testing::Test {
 protected:
  void InitProtocolHandlerImpl(const size_t period_msec,
                               const size_t max_messages,
                               bool malformed_message_filtering = false,
                               const size_t malformd_period_msec = 0u,
                               const size_t malformd_max_messages = 0u,
                               const int32_t multiframe_waiting_timeout = 0,
                               const size_t maximum_payload_size = 0u) {
    ON_CALL(protocol_handler_settings_mock, maximum_payload_size())
        .WillByDefault(Return(maximum_payload_size));
    ON_CALL(protocol_handler_settings_mock, message_frequency_time())
        .WillByDefault(Return(period_msec));
    ON_CALL(protocol_handler_settings_mock, message_frequency_count())
        .WillByDefault(Return(max_messages));
    ON_CALL(protocol_handler_settings_mock, malformed_message_filtering())
        .WillByDefault(Return(malformed_message_filtering));
    ON_CALL(protocol_handler_settings_mock, malformed_frequency_time())
        .WillByDefault(Return(malformd_period_msec));
    ON_CALL(protocol_handler_settings_mock, malformed_frequency_count())
        .WillByDefault(Return(malformd_max_messages));
    ON_CALL(protocol_handler_settings_mock, multiframe_waiting_timeout())
        .WillByDefault(Return(multiframe_waiting_timeout));
    protocol_handler_impl.reset(
        new ProtocolHandlerImpl(protocol_handler_settings_mock,
                                session_observer_mock,
                                connection_handler_mock,
                                transport_manager_mock));
    tm_listener = protocol_handler_impl.get();
  }

  void SetUp() OVERRIDE {
    InitProtocolHandlerImpl(0u, 0u);
    connection_id = 0xAu;
    session_id = 0xFFu;
    connection_key = 0xFF00AAu;
    message_id = 0xABCDEFu;
    some_data.resize(256, 0xAB);

    // Expect ConnectionHandler support methods call (conversion, check
    // heartbeat)
    ON_CALL(session_observer_mock, KeyFromPair(connection_id, _))
        // return some connection_key
        .WillByDefault(Return(connection_key));
    ON_CALL(session_observer_mock, IsHeartBeatSupported(connection_id, _))
        // return false to avoid call KeepConnectionAlive
        .WillByDefault(Return(false));
  }

  void TearDown() OVERRIDE {
    // Wait call methods in thread
    testing::Mock::AsyncVerifyAndClearExpectations(10000);
  }

  // Emulate connection establish
  void AddConnection() {
    tm_listener->OnConnectionEstablished(DeviceInfo(DeviceHandle(1u),
                                                    std::string("mac"),
                                                    std::string("name"),
                                                    std::string("BTMAC")),
                                         connection_id);
  }
  void AddSession() {
    AddConnection();
    const ServiceType start_service = kRpc;
#ifdef ENABLE_SECURITY
    // For enabled protection callback shall use protection ON
    const bool callback_protection_flag = PROTECTION_ON;
#else
    // For disabled protection callback shall ignore protection income flad and
    // use protection OFF
    const bool callback_protection_flag = PROTECTION_OFF;
#endif  // ENABLE_SECURITY
    // Expect ConnectionHandler check
    EXPECT_CALL(session_observer_mock,
                OnSessionStartedCallback(connection_id,
                                         NEW_SESSION_ID,
                                         start_service,
                                         callback_protection_flag,
                                         _))
        .
        // Return sessions start success
        WillOnce(Return(session_id));

    // Expect send Ack with PROTECTION_OFF (on no Security Manager)
    EXPECT_CALL(transport_manager_mock,
                SendMessageToDevice(ControlMessage(FRAME_DATA_START_SERVICE_ACK,
                                                   PROTECTION_OFF)))
        .WillOnce(Return(E_SUCCESS));

    SendControlMessage(
        PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  }

#ifdef ENABLE_SECURITY
  // Emulate security manager initilization establish
  void AddSecurityManager() {
    protocol_handler_impl->set_security_manager(&security_manager_mock);
  }
#endif  // ENABLE_SECURITY
  void SendTMMessage(uint8_t connection_id,
                     uint8_t version,
                     bool protection,
                     uint8_t frameType,
                     uint8_t serviceType,
                     uint8_t frameData,
                     uint8_t sessionId,
                     uint32_t dataSize,
                     uint32_t messageID,
                     const uint8_t* data = 0) {
    // Create packet
    const ProtocolPacket packet(connection_id,
                                version,
                                protection,
                                frameType,
                                serviceType,
                                frameData,
                                sessionId,
                                dataSize,
                                messageID,
                                data);
    // Emulate receive packet from transoprt manager
    tm_listener->OnTMMessageReceived(packet.serializePacket());
  }

  void SetProtocolVersion2() {
    // Set protocol version 2
    ON_CALL(protocol_handler_settings_mock, max_supported_protocol_version())
        .WillByDefault(Return(PROTOCOL_VERSION_2));
  }

  void SendControlMessage(bool protection,
                          uint8_t service_type,
                          uint8_t sessionId,
                          uint32_t frame_data,
                          uint32_t dataSize = 0u,
                          const uint8_t* data = NULL) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  protection,
                  FRAME_TYPE_CONTROL,
                  service_type,
                  frame_data,
                  sessionId,
                  dataSize,
                  message_id,
                  data);
  }

  testing::NiceMock<MockProtocolHandlerSettings> protocol_handler_settings_mock;
  TransportManagerListener* tm_listener;
  // Uniq connection
  ::transport_manager::ConnectionUID connection_id;
  // Id of established session
  uint8_t session_id;
  // Uniq id as connection_id and session_id in one
  uint32_t connection_key;
  uint32_t message_id;
  UCharDataVector some_data;
  // Strict mocks (same as all methods EXPECT_CALL().Times(0))
  testing::NiceMock<connection_handler_test::MockConnectionHandler>
      connection_handler_mock;
  testing::NiceMock<transport_manager_test::MockTransportManager>
      transport_manager_mock;
  testing::NiceMock<protocol_handler_test::MockSessionObserver>
      session_observer_mock;
#ifdef ENABLE_SECURITY
  testing::NiceMock<security_manager_test::MockSecurityManager>
      security_manager_mock;
  testing::NiceMock<security_manager_test::MockSSLContext> ssl_context_mock;
#endif  // ENABLE_SECURITY
  ::utils::SharedPtr<ProtocolHandlerImpl> protocol_handler_impl;
};

#ifdef ENABLE_SECURITY
class OnHandshakeDoneFunctor {
 public:
  OnHandshakeDoneFunctor(const uint32_t connection_key,
                         security_manager::SSLContext::HandshakeResult error)
      : connection_key(connection_key), result(error) {}
  void operator()(security_manager::SecurityManagerListener* listener) const {
    listener->OnHandshakeDone(connection_key, result);
  }

 private:
  const uint32_t connection_key;
  const security_manager::SSLContext::HandshakeResult result;
};
#endif  // ENABLE_SECURITY

/*
 * ProtocolHandler shall skip empty message
 */
TEST_F(ProtocolHandlerImplTest, RecieveEmptyRawMessage) {
  tm_listener->OnTMMessageReceived(RawMessagePtr());
}
/*
 * ProtocolHandler shall disconnect on no connection
 */
TEST_F(ProtocolHandlerImplTest, RecieveOnUnknownConnection) {
  EXPECT_CALL(transport_manager_mock, DisconnectForce(connection_id))
      .WillOnce(Return(E_SUCCESS));

  SendTMMessage(connection_id,
                PROTOCOL_VERSION_3,
                PROTECTION_OFF,
                FRAME_TYPE_CONTROL,
                kRpc,
                FRAME_DATA_START_SERVICE,
                NEW_SESSION_ID,
                0,
                message_id);
}
/*
 * ProtocolHandler shall send NAck on session_observer rejection
 * Check protection flag OFF for all services from kControl to kBulk
 */
TEST_F(ProtocolHandlerImplTest,
       StartSession_Unprotected_SessionObserverReject) {
  const int call_times = 5;
  AddConnection();
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(connection_id,
                               NEW_SESSION_ID,
                               AnyOf(kControl, kRpc, kAudio, kMobileNav, kBulk),
                               PROTECTION_OFF,
                               _))
      .Times(call_times)
      .
      // Return sessions start rejection
      WillRepeatedly(Return(SESSION_START_REJECT));

  // Expect send NAck
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ControlMessage(FRAME_DATA_START_SERVICE_NACK,
                                                 PROTECTION_OFF)))
      .Times(call_times)
      .WillRepeatedly(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_OFF, kControl, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_OFF, kRpc, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_OFF, kAudio, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_OFF, kMobileNav, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_OFF, kBulk, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send NAck on session_observer rejection
 * Emulate getting PROTECTION_ON and check protection flag OFF in NAck
 * For ENABLE_SECURITY=OFF session_observer shall be called with protection flag
 * OFF
 */
TEST_F(ProtocolHandlerImplTest, StartSession_Protected_SessionObserverReject) {
  const int call_times = 5;
  AddConnection();
#ifdef ENABLE_SECURITY
  // For enabled protection callback shall use protection ON
  const bool callback_protection_flag = PROTECTION_ON;
#else
  // For disabled protection callback shall ignore protection income flag and
  // use protection OFF
  const bool callback_protection_flag = PROTECTION_OFF;
#endif  // ENABLE_SECURITY
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(connection_id,
                               NEW_SESSION_ID,
                               AnyOf(kControl, kRpc, kAudio, kMobileNav, kBulk),
                               callback_protection_flag,
                               _))
      .Times(call_times)
      .
      // Return sessions start rejection
      WillRepeatedly(Return(SESSION_START_REJECT));

  // Expect send NAck with encryption OFF
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ControlMessage(FRAME_DATA_START_SERVICE_NACK,
                                                 PROTECTION_OFF)))
      .Times(call_times)
      .WillRepeatedly(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, kControl, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_ON, kRpc, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_ON, kAudio, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_ON, kMobileNav, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
  SendControlMessage(
      PROTECTION_ON, kBulk, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack on session_observer accept
 * Check protection flag OFF
 */
TEST_F(ProtocolHandlerImplTest,
       StartSession_Unprotected_SessionObserverAccept) {
  AddConnection();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_OFF, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  SetProtocolVersion2();
  // Expect send Ack
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_OFF, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack on session_observer accept
 * Emulate getting PROTECTION_ON and check protection flag OFF in Ack
 * For ENABLE_SECURITY=OFF session_observer shall be called with protection flag
 * OFF
 */
TEST_F(ProtocolHandlerImplTest, StartSession_Protected_SessionObserverAccept) {
  SetProtocolVersion2();
  AddSession();
}
// TODO(EZamakhov): add test for get_hash_id/set_hash_id from
// protocol_handler_impl.cc
/*
 * ProtocolHandler shall send NAck on session_observer rejection
 */
TEST_F(ProtocolHandlerImplTest, EndSession_SessionObserverReject) {
  AddSession();
  const ServiceType service = kRpc;

  // Expect ConnectionHandler check
  EXPECT_CALL(session_observer_mock,
              OnSessionEndedCallback(connection_id, session_id, _, service))
      .
      // reject session start
      WillOnce(Return(SESSION_START_REJECT));

  SetProtocolVersion2();
  // Expect send NAck
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_END_SERVICE_NACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_OFF, service, session_id, FRAME_DATA_END_SERVICE);
}
/*
 * ProtocolHandler shall send NAck on wrong hash code
 */
TEST_F(ProtocolHandlerImplTest, EndSession_Success) {
  AddSession();
  const ServiceType service = kRpc;

  // Expect ConnectionHandler check
  EXPECT_CALL(session_observer_mock,
              OnSessionEndedCallback(connection_id, session_id, _, service))
      .
      // return sessions start success
      WillOnce(Return(connection_key));

  SetProtocolVersion2();
  // Expect send Ack
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_END_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_OFF, service, session_id, FRAME_DATA_END_SERVICE);
}

#ifdef ENABLE_SECURITY
/*
 * ProtocolHandler shall not call Security logics with Protocol version 1
 * Check session_observer with PROTECTION_OFF and Ack with PROTECTION_OFF
 */
TEST_F(ProtocolHandlerImplTest, SecurityEnable_StartSessionProtocoloV1) {
  AddConnection();
  // Add security manager
  AddSecurityManager();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_OFF, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  SetProtocolVersion2();
  // Expect send Ack with PROTECTION_OFF (on no Security Manager)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendTMMessage(connection_id,
                PROTOCOL_VERSION_1,
                PROTECTION_ON,
                FRAME_TYPE_CONTROL,
                start_service,
                FRAME_DATA_START_SERVICE,
                NEW_SESSION_ID,
                0,
                message_id);
}
/*
 * ProtocolHandler shall not call Security logics on start session with
 * PROTECTION_OFF
 */
TEST_F(ProtocolHandlerImplTest, SecurityEnable_StartSessionUnprotected) {
  AddConnection();
  // Add security manager
  AddSecurityManager();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_OFF, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  SetProtocolVersion2();
  // Expect send Ack with PROTECTION_OFF (on no Security Manager)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_OFF, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_OFF on fail SLL creation
 */
TEST_F(ProtocolHandlerImplTest, SecurityEnable_StartSessionProtected_Fail) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  SetProtocolVersion2();
  // Expect start protection for unprotected session
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return fail protection
      WillOnce(ReturnNull());

  // Expect send Ack with PROTECTION_OFF (on fail SLL creation)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_ON on already established and
 * initialized SLLContext
 */
TEST_F(ProtocolHandlerImplTest,
       SecurityEnable_StartSessionProtected_SSLInitialized) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  SetProtocolVersion2();
  // call new SSLContext creation
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return new SSLContext
      WillOnce(Return(&ssl_context_mock));

  // Initilization check
  EXPECT_CALL(ssl_context_mock, IsInitCompleted())
      .
      // emulate SSL is initilized
      WillOnce(Return(true));

  // Expect service protection enable
  EXPECT_CALL(session_observer_mock,
              SetProtectionFlag(connection_key, start_service));

  // Expect send Ack with PROTECTION_ON (on SSL is initilized)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_ON)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_OFF on session handshhake fail
 */
TEST_F(ProtocolHandlerImplTest,
       SecurityEnable_StartSessionProtected_HandshakeFail) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;
  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  std::vector<int> services;
  // TODO(AKutsan) : APPLINK-21398 use named constants instead of magic numbers
  services.push_back(0x0A);
  services.push_back(0x0B);
  ON_CALL(protocol_handler_settings_mock, force_protected_service())
      .WillByDefault(ReturnRefOfCopy(services));

  // call new SSLContext creation
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return new SSLContext
      WillOnce(Return(&ssl_context_mock));

  // Initilization check
  EXPECT_CALL(ssl_context_mock, IsInitCompleted())
      .
      // emulate SSL is not initilized
      WillOnce(Return(false));

  // Pending handshake check
  EXPECT_CALL(ssl_context_mock, IsHandshakePending())
      .
      // emulate is pending
      WillOnce(Return(true));

  // Expect add listener for handshake result
  EXPECT_CALL(security_manager_mock, AddListener(_))
      // Emulate handshake fail
      .WillOnce(Invoke(OnHandshakeDoneFunctor(
          connection_key,
          security_manager::SSLContext::Handshake_Result_Fail)));

  // Listener check SSLContext
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(connection_key, start_service))
      .
      // Emulate protection for service is not enabled
      WillOnce(ReturnNull());

  // Expect send Ack with PROTECTION_OFF (on fail handshake)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_ON on session handshhake
 * success
 */
TEST_F(ProtocolHandlerImplTest,
       SecurityEnable_StartSessionProtected_HandshakeSuccess) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;

  // No services are protected
  std::vector<int> services;
  ON_CALL(protocol_handler_settings_mock, force_protected_service())
      .WillByDefault(ReturnRefOfCopy(services));

  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  // call new SSLContext creation
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return new SSLContext
      WillOnce(Return(&ssl_context_mock));

  // Initilization check
  EXPECT_CALL(ssl_context_mock, IsInitCompleted())
      .
      // emulate SSL is not initilized
      WillOnce(Return(false));

  // Pending handshake check
  EXPECT_CALL(ssl_context_mock, IsHandshakePending())
      .
      // emulate is pending
      WillOnce(Return(true));

  // Expect add listener for handshake result
  EXPECT_CALL(security_manager_mock, AddListener(_))
      // Emulate handshake fail
      .WillOnce(Invoke(OnHandshakeDoneFunctor(
          connection_key,
          security_manager::SSLContext::Handshake_Result_Success)));

  // Listener check SSLContext
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(connection_key, start_service))
      .
      // Emulate protection for service is not enabled
      WillOnce(ReturnNull());

  // Expect service protection enable
  EXPECT_CALL(session_observer_mock,
              SetProtectionFlag(connection_key, start_service));

  // Expect send Ack with PROTECTION_OFF (on fail handshake)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_ON)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_ON on session handshhake
 * success
 */
TEST_F(
    ProtocolHandlerImplTest,
    SecurityEnable_StartSessionProtected_HandshakeSuccess_ServiceProtectedBefore) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;

  std::vector<int> services;
  ON_CALL(protocol_handler_settings_mock, force_protected_service())
      .WillByDefault(ReturnRefOfCopy(services));

  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  // call new SSLContext creation
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return new SSLContext
      WillOnce(Return(&ssl_context_mock));

  // Initilization check
  EXPECT_CALL(ssl_context_mock, IsInitCompleted())
      .
      // emulate SSL is not initilized
      WillOnce(Return(false));

  // Pending handshake check
  EXPECT_CALL(ssl_context_mock, IsHandshakePending())
      .
      // emulate is pending
      WillOnce(Return(true));

  // Expect add listener for handshake result
  EXPECT_CALL(security_manager_mock, AddListener(_))
      // Emulate handshake fail
      .WillOnce(Invoke(OnHandshakeDoneFunctor(
          connection_key,
          security_manager::SSLContext::Handshake_Result_Success)));

  // Listener check SSLContext
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(connection_key, start_service))
      .
      // Emulate protection for service is not enabled
      WillOnce(ReturnNull());

  // Expect service protection enable
  EXPECT_CALL(session_observer_mock,
              SetProtectionFlag(connection_key, start_service));

  // Expect send Ack with PROTECTION_OFF (on fail handshake)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_ON)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
/*
 * ProtocolHandler shall send Ack with PROTECTION_ON on session handshhake
 * success
 */
TEST_F(ProtocolHandlerImplTest,
       SecurityEnable_StartSessionProtected_HandshakeSuccess_SSLIsNotPending) {
  AddConnection();
  AddSecurityManager();
  const ServiceType start_service = kRpc;

  std::vector<int> services;
  ON_CALL(protocol_handler_settings_mock, force_protected_service())
      .WillByDefault(ReturnRefOfCopy(services));

  // Expect ConnectionHandler check
  EXPECT_CALL(
      session_observer_mock,
      OnSessionStartedCallback(
          connection_id, NEW_SESSION_ID, start_service, PROTECTION_ON, _))
      .
      // Return sessions start success
      WillOnce(Return(session_id));

  // call new SSLContext creation
  EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key))
      .
      // Return new SSLContext
      WillOnce(Return(&ssl_context_mock));

  // Initilization check
  EXPECT_CALL(ssl_context_mock, IsInitCompleted())
      .
      // emulate SSL is not initilized
      WillOnce(Return(false));

  // Pending handshake check
  EXPECT_CALL(ssl_context_mock, IsHandshakePending())
      .
      // emulate is pending
      WillOnce(Return(false));

  // Wait restart handshake operation
  EXPECT_CALL(security_manager_mock, StartHandshake(connection_key));

  // Expect add listener for handshake result
  EXPECT_CALL(security_manager_mock, AddListener(_))
      // Emulate handshake fail
      .WillOnce(Invoke(OnHandshakeDoneFunctor(
          connection_key,
          security_manager::SSLContext::Handshake_Result_Success)));

  // Listener check SSLContext
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(connection_key, start_service))
      .
      // Emulate protection for service is not enabled
      WillOnce(ReturnNull());

  // Expect service protection enable
  EXPECT_CALL(session_observer_mock,
              SetProtectionFlag(connection_key, start_service));

  // Expect send Ack with PROTECTION_OFF (on fail handshake)
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(
                  ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_ON)))
      .WillOnce(Return(E_SUCCESS));

  SendControlMessage(
      PROTECTION_ON, start_service, NEW_SESSION_ID, FRAME_DATA_START_SERVICE);
}
#endif  // ENABLE_SECURITY

TEST_F(ProtocolHandlerImplTest, FloodVerification) {
  const size_t period_msec = 10000;
  const size_t max_messages = 1000;
  InitProtocolHandlerImpl(period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect flood notification to CH
  EXPECT_CALL(session_observer_mock, OnApplicationFloodCallBack(connection_key))
      .Times(1);

  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}
TEST_F(ProtocolHandlerImplTest, FloodVerification_ThresholdValue) {
  const size_t period_msec = 10000;
  const size_t max_messages = 1000;
  InitProtocolHandlerImpl(period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect NO flood notification to CH
  EXPECT_CALL(session_observer_mock, OnApplicationFloodCallBack(connection_key))
      .Times(0);
  for (size_t i = 0; i < max_messages - 1; ++i) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}
TEST_F(ProtocolHandlerImplTest, FloodVerification_VideoFrameSkip) {
  const size_t period_msec = 10000;
  const size_t max_messages = 1000;
  InitProtocolHandlerImpl(period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect NO flood notification to CH on video data streaming
  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kMobileNav,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}
TEST_F(ProtocolHandlerImplTest, FloodVerification_AudioFrameSkip) {
  const size_t period_msec = 10000;
  const size_t max_messages = 1000;
  InitProtocolHandlerImpl(period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect NO flood notification to CH on video data streaming
  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kAudio,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}
TEST_F(ProtocolHandlerImplTest, FloodVerificationDisable) {
  const size_t period_msec = 0;
  const size_t max_messages = 0;
  InitProtocolHandlerImpl(period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect NO flood notification to session observer
  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_3,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest, MalformedVerificationDisable) {
  const size_t period_msec = 10000;
  const size_t max_messages = 100;
  InitProtocolHandlerImpl(0u, 0u, false, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(max_messages);

  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  for (size_t i = 0; i < max_messages; ++i) {
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest, MalformedLimitVerification) {
  const size_t period_msec = 10000;
  const size_t max_messages = 100;
  InitProtocolHandlerImpl(0u, 0u, true, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(1);

  // Sending malformed packets
  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  for (size_t i = 0; i < max_messages * 2; ++i) {
    // Malformed message
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
    // Common message
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest, MalformedLimitVerification_MalformedStock) {
  const size_t period_msec = 10000;
  const size_t max_messages = 100;
  InitProtocolHandlerImpl(0u, 0u, true, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(1);

  // Sending malformed packets
  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  const uint8_t malformed_frame_type = FRAME_TYPE_MAX_VALUE;
  const uint8_t malformed_service_type = kInvalidServiceType;
  for (size_t i = 0; i < max_messages * 2; ++i) {
    // Malformed message 1
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
    // Malformed message 2
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  malformed_frame_type,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
    // Malformed message 3
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  malformed_service_type,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);

    // Common message
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest, MalformedLimitVerification_MalformedOnly) {
  const size_t period_msec = 10000;
  const size_t max_messages = 100;
  InitProtocolHandlerImpl(0u, 0u, true, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect NO malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(0);

  // Sending malformed packets
  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  const uint8_t malformed_frame_type = FRAME_TYPE_MAX_VALUE;
  const uint8_t malformed_service_type = kInvalidServiceType;
  for (size_t i = 0; i < max_messages * 2; ++i) {
    // Malformed message 1
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
    // Malformed message 2
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  malformed_frame_type,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
    // Malformed message 3
    SendTMMessage(connection_id,
                  PROTOCOL_VERSION_1,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  malformed_service_type,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);

    // No common message
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest, MalformedLimitVerification_NullTimePeriod) {
  const size_t period_msec = 0;
  const size_t max_messages = 1000;
  InitProtocolHandlerImpl(0u, 0u, true, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect no malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(0);

  // Sending malformed packets
  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}
TEST_F(ProtocolHandlerImplTest, MalformedLimitVerification_NullCount) {
  const size_t period_msec = 10000;
  const size_t max_messages = 0;
  InitProtocolHandlerImpl(0u, 0u, true, period_msec, max_messages);
  AddConnection();
  AddSession();

  // Expect no malformed notification to CH
  EXPECT_CALL(session_observer_mock, OnMalformedMessageCallback(connection_id))
      .Times(0);

  // Sending malformed packets
  const uint8_t malformed_version = PROTOCOL_VERSION_MAX;
  for (size_t i = 0; i < max_messages + 1; ++i) {
    SendTMMessage(connection_id,
                  malformed_version,
                  PROTECTION_OFF,
                  FRAME_TYPE_SINGLE,
                  kControl,
                  FRAME_DATA_SINGLE,
                  session_id,
                  some_data.size(),
                  message_id,
                  &some_data[0]);
  }
#ifdef OS_WIN32
  Sleep(1000);
#endif
}

TEST_F(ProtocolHandlerImplTest,
       SendEndServicePrivate_NoConnection_MessageNotSent) {
  // Expect check connection with ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock,
              ProtocolVersionUsed(connection_id, session_id, _))
      .WillOnce(Return(false));
  // Expect not send End Service
  EXPECT_CALL(transport_manager_mock, SendMessageToDevice(_)).Times(0);
  // Act
  protocol_handler_impl->SendEndSession(connection_id, session_id);
}

TEST_F(ProtocolHandlerImplTest, SendEndServicePrivate_EndSession_MessageSent) {
  // Arrange
  AddSession();
  // Expect check connection with ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock,
              ProtocolVersionUsed(connection_id, session_id, _))
      .WillOnce(Return(true));
  // Expect send End Service
  EXPECT_CALL(
      transport_manager_mock,
      SendMessageToDevice(ExpectedMessage(
          FRAME_TYPE_CONTROL, FRAME_DATA_END_SERVICE, PROTECTION_OFF, kRpc)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendEndSession(connection_id, session_id);
}

TEST_F(ProtocolHandlerImplTest,
       SendEndServicePrivate_ServiceTypeControl_MessageSent) {
  // Arrange
  AddSession();
  // Expect check connection with ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock,
              ProtocolVersionUsed(connection_id, session_id, _))
      .WillOnce(Return(true));
  // Expect send End Service
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(FRAME_TYPE_CONTROL,
                                                  FRAME_DATA_END_SERVICE,
                                                  PROTECTION_OFF,
                                                  kControl)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendEndService(connection_id, session_id, kControl);
}

TEST_F(ProtocolHandlerImplTest, SendHeartBeat_NoConnection_NotSent) {
  // Expect check connection with ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock,
              ProtocolVersionUsed(connection_id, session_id, _))
      .WillOnce(Return(false));
  // Expect not send HeartBeat
  EXPECT_CALL(transport_manager_mock, SendMessageToDevice(_)).Times(0);
  // Act
  protocol_handler_impl->SendHeartBeat(connection_id, session_id);
}

TEST_F(ProtocolHandlerImplTest, SendHeartBeat_Successful) {
  // Arrange
  AddSession();
  // Expect check connection with ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock,
              ProtocolVersionUsed(connection_id, session_id, _))
      .WillOnce(Return(true));
  // Expect send HeartBeat
  EXPECT_CALL(
      transport_manager_mock,
      SendMessageToDevice(ExpectedMessage(
          FRAME_TYPE_CONTROL, FRAME_DATA_HEART_BEAT, PROTECTION_OFF, kControl)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendHeartBeat(connection_id, session_id);
}

TEST_F(ProtocolHandlerImplTest, SendHeartBeatAck_Successful) {
  // Arrange
  AddSession();
  // Expect double check connection and protocol version with
  // ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(connection_id, _, _))
      .WillRepeatedly(
          DoAll(SetArgReferee<2>(PROTOCOL_VERSION_3), Return(true)));
  // Expect send HeartBeatAck
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(FRAME_TYPE_CONTROL,
                                                  FRAME_DATA_HEART_BEAT_ACK,
                                                  PROTECTION_OFF,
                                                  kControl)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  SendControlMessage(
      PROTECTION_OFF, kControl, session_id, FRAME_DATA_HEART_BEAT);
}

TEST_F(ProtocolHandlerImplTest, SendHeartBeatAck_WrongProtocolVersion_NotSent) {
  // Arrange
  AddSession();
  // Expect two checks of connection and protocol version with
  // ProtocolVersionUsed
  EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(connection_id, _, _))
      .Times(2)
      .WillRepeatedly(
          DoAll(SetArgReferee<2>(PROTOCOL_VERSION_1), Return(true)));
  // Expect not send HeartBeatAck
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(FRAME_TYPE_CONTROL,
                                                  FRAME_DATA_HEART_BEAT_ACK,
                                                  PROTECTION_OFF,
                                                  kControl))).Times(0);
  // Act
  SendControlMessage(
      PROTECTION_OFF, kControl, session_id, FRAME_DATA_HEART_BEAT);
  SendControlMessage(
      PROTECTION_OFF, kControl, session_id, FRAME_DATA_HEART_BEAT);
}

TEST_F(ProtocolHandlerImplTest,
       SendMessageToMobileApp_SendSingleControlMessage) {
  // Arrange
  AddSession();
  const bool is_final = true;
  const uint32_t total_data_size = 1;
  UCharDataVector data(total_data_size);
  RawMessagePtr message = utils::MakeShared<RawMessage>(
      connection_key, PROTOCOL_VERSION_3, &data[0], total_data_size, kControl);
  // Expect getting pair from key from session observer
  EXPECT_CALL(session_observer_mock,
              PairFromKey(message->connection_key(), _, _))
      .WillOnce(
          DoAll(SetArgPointee<1>(connection_id), SetArgPointee<2>(session_id)));
// Expect getting ssl context
#ifdef ENABLE_SECURITY
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(message->connection_key(), message->service_type()))
      .WillOnce(Return(&ssl_context_mock));
#endif  // ENABLE_SECURITY
  // Expect send message to mobile
  EXPECT_CALL(
      transport_manager_mock,
      SendMessageToDevice(ExpectedMessage(
          FRAME_TYPE_SINGLE, FRAME_DATA_SINGLE, PROTECTION_OFF, kControl)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendMessageToMobileApp(message, is_final);
}

TEST_F(ProtocolHandlerImplTest,
       SendMessageToMobileApp_SendSingleNonControlMessage) {
  // Arrange
  AddSession();
  const bool is_final = true;
  const uint32_t total_data_size = 1;
  UCharDataVector data(total_data_size);
  RawMessagePtr message = utils::MakeShared<RawMessage>(
      connection_key, PROTOCOL_VERSION_3, &data[0], total_data_size, kRpc);
  // Expect getting pair from key from session observer
  EXPECT_CALL(session_observer_mock,
              PairFromKey(message->connection_key(), _, _))
      .WillOnce(
          DoAll(SetArgPointee<1>(connection_id), SetArgPointee<2>(session_id)));
// Expect getting ssl context
#ifdef ENABLE_SECURITY
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(message->connection_key(), message->service_type()))
      .Times(2)
      .WillRepeatedly(Return(&ssl_context_mock));
  AddSecurityManager();
#endif  // ENABLE_SECURITY
  // Expect send message to mobile
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(
                  FRAME_TYPE_SINGLE, FRAME_DATA_SINGLE, PROTECTION_OFF, kRpc)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendMessageToMobileApp(message, is_final);
}

TEST_F(ProtocolHandlerImplTest, SendMessageToMobileApp_SendMultiframeMessage) {
  // Arrange
  AddSession();
  const bool is_final = true;
  const uint32_t total_data_size = MAXIMUM_FRAME_DATA_V2_SIZE * 2;
  UCharDataVector data(total_data_size);
  const uint8_t first_consecutive_frame = 0x01;
  RawMessagePtr message = utils::MakeShared<RawMessage>(
      connection_key, PROTOCOL_VERSION_3, &data[0], total_data_size, kBulk);
  // Expect getting pair from key from session observer
  EXPECT_CALL(session_observer_mock,
              PairFromKey(message->connection_key(), _, _))
      .WillOnce(
          DoAll(SetArgPointee<1>(connection_id), SetArgPointee<2>(session_id)));
// Expect getting ssl context
#ifdef ENABLE_SECURITY
  EXPECT_CALL(session_observer_mock,
              GetSSLContext(message->connection_key(), message->service_type()))
      .Times(4)
      .WillRepeatedly(Return(&ssl_context_mock));
  AddSecurityManager();
#endif  // ENABLE_SECURITY
  // Expect sending message frame by frame to mobile
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(
                  FRAME_TYPE_FIRST, FRAME_DATA_FIRST, PROTECTION_OFF, kBulk)))
      .WillOnce(Return(E_SUCCESS));
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(FRAME_TYPE_CONSECUTIVE,
                                                  first_consecutive_frame,
                                                  PROTECTION_OFF,
                                                  kBulk)))
      .WillOnce(Return(E_SUCCESS));
  EXPECT_CALL(transport_manager_mock,
              SendMessageToDevice(ExpectedMessage(FRAME_TYPE_CONSECUTIVE,
                                                  FRAME_DATA_LAST_CONSECUTIVE,
                                                  PROTECTION_OFF,
                                                  kBulk)))
      .WillOnce(Return(E_SUCCESS));
  // Act
  protocol_handler_impl->SendMessageToMobileApp(message, is_final);
}

}  // namespace protocol_handler_test
}  // namespace components
}  // namespace test
